// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unlit/SSRPostProcess"
{
    Properties
    {
        _MainTex ("Base (RGB)", 2D) = "white" {}
        //_CameraDepthTexture ("Base (RGBA)", 2D) = "black" {}
    }
    SubShader
    {
        Pass
        {
            ZTest Always
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
                float4 csRay : POSITION2;
                float4 worldPos : POSITION3;
            };

            inline float4 UnityObjectToClipPosRespectW(in float4 pos)
            {
                return mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, pos));
            }

            sampler2D _MainTex;
            sampler2D _CameraDepthNormalsTexture;
            float4x4 _CameraMatrix;
            float4x4 _CameraMatrixInv;
            float4x4 _InvProjectMatrix;
            float4x4 _ProjectMatrix;

            bool rayMarch(float3 csRayOri, float3 csRayDir, float4x4 projectMat, sampler2D _depthNormalTex, float pixelZThickness, float farPlaneZ, float nearPlaneZ, float stride, float maxSteps, float maxDistance, 
                            out float2 hitPos, out float times)
            {
                float rayLength = ((csRayOri.z + csRayDir.z * maxDistance) > nearPlaneZ)?(nearPlaneZ - csRayOri.z)/csRayDir.z:maxDistance;
                csRayOri = csRayOri + csRayDir * 0.03;
                float3 csRayEnd = csRayOri + csRayDir * rayLength;

                float4 H0 = mul(projectMat, float4(csRayOri, 1));
                float4 H1 = mul(projectMat, float4(csRayEnd, 1));
                float k0 = 1.0 / H0.w;
                float k1 = 1.0 / H1.w;

                float3 Q0 = csRayOri * k0, Q1 = csRayEnd * k1;      //Q is in cs space
                float2 P0 = H0.xy * k0, P1 = H1.xy * k1;            //P is in ndc
                float2 delta = P1 - P0;
                bool permute = false;
                if(abs(delta.y) > abs(delta.x))
                {
                    permute = true;
                    float t = delta.y;delta.y = delta.x;delta.x = t;
                    P0 = P0.yx;
                    P1 = P1.yx;
                }
                float deltaDir = sign(delta.x);
                float invDx = deltaDir / delta.x;

                float3 dQ = (Q1 - Q0) * invDx;
                float dK = (k1 - k0) * invDx;
                float2 dP = float2(deltaDir, delta.y * invDx);

                dP *= stride; dQ *= stride; dK *= stride;

                float end = P1.x * deltaDir;

                float k = k0, stepCount = 0.0, prevZMaxEstimate = csRayOri.z;
                float3 Q = Q0;
                float rayZMin = prevZMaxEstimate, rayZMax = prevZMaxEstimate;
                float sceneZMax = rayZMax + 100;
                float3 normal;
                float linear01Depth;
                bool insects = false;

                for(float2 P = P0; (P.x * deltaDir) <= end && stepCount < maxSteps && insects == false;
                        ++stepCount) {
                    P += dP, Q.z += dQ.z, k += dK;
                    rayZMin = prevZMaxEstimate;
                    rayZMax = (dQ.z * 0.5 + Q.z) / (dK * 0.5 + k);
                    prevZMaxEstimate = rayZMax;
                    if(rayZMin > rayZMax)
                    {
                        float t = rayZMin; rayZMin = rayZMax; rayZMax = t;
                    }
                    hitPos = permute ? P.yx:P;
                    DecodeDepthNormal(tex2D(_CameraDepthNormalsTexture, hitPos * 0.5 + 0.5), linear01Depth, normal);
                    sceneZMax = linear01Depth * -farPlaneZ; 
                    insects = (rayZMax >= sceneZMax - pixelZThickness) && (rayZMin < sceneZMax);
                }

                Q.xy += dQ.xy * stepCount;
                times = Q1 / k0 * 0.005;  
                //hitPos = permute ? P0.yx:P0; 
                return insects;
            }

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPosRespectW(v.vertex);
                o.uv = v.uv;
                float4 ray = float4(v.uv * 2.0 - 1.0, 1.0, 1.0);
                ray = mul(_InvProjectMatrix, ray);
                ray = ray / ray.w;          //convert to camera space
                o.csRay = ray;
                return o;
            }

            fixed4 frag3 (v2f i) : SV_Target
            {
                // sample the texture
                float3 normal;
                float linear01Depth;
                DecodeDepthNormal(tex2D(_CameraDepthNormalsTexture, i.uv), linear01Depth, normal);
                float3 rayOri = i.csRay * linear01Depth;
                float4 clipSpace = mul(_ProjectMatrix, float4(rayOri, 1));
                float4 ndcSpace = clipSpace / clipSpace.w;
                float4 wsRayOri = mul(_CameraMatrixInv, float4(rayOri, 1));
                fixed4 col = tex2D(_MainTex, i.uv);
                if(wsRayOri.y < 0.1)
                {
                    col = lerp(col, float4(0, 0, 0, 1), 0.7);
                }
                //col.rgb = wsRayOri.xyz;
                //tex2D(_MainTex, i.uv);// float4(i.uv.x,i.uv.x,i.uv.x,1);
                //col.rgba = float4(linear01Depth, linear01Depth, linear01Depth, linear01Depth);
                //col.rgba = float4(i.uv, 1.0, 1.0);
                col.a = 1;
                return col;
            }

            float3 adjustNormal(float3 normal, float a, float z, float t, float time)
            {
                float3x3 cam = _CameraMatrix;
                float3 up = mul(cam, float3(0.0, 1.0, 0.0));
                normal += up * (a + a * sin(2.0 * 3.14159 * z / t + time));
                return normalize(normal);
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                float3 normal;
                float linear01Depth;
                float stepCount;
                DecodeDepthNormal(tex2D(_CameraDepthNormalsTexture, i.uv), linear01Depth, normal);
                float3 rayOri = i.csRay * linear01Depth;
                //normal = adjustNormal(normal, 100.0, rayOri.z, 0.01, _Time.x);
                float3 rayDir = -normalize(rayOri); 
                float3 reflectDir = normalize(2.0 * dot(rayDir, normal) * normal - rayDir);
                float4 clipSpace = mul(_ProjectMatrix, float4(rayOri, 1));
                float4 ndcSpace = clipSpace / clipSpace.w;
                float4 wsRayOri = mul(_CameraMatrixInv, float4(rayOri, 1));
                fixed4 col = tex2D(_MainTex, i.uv);
                float2 pt = float2(0.1, 0.1);
                if(wsRayOri.y < 0.04)
                {
                    col.rgb = fixed3(0.1, 0.1, 0.4);  
                    if(rayMarch(rayOri, reflectDir, _ProjectMatrix, _CameraDepthNormalsTexture, 0.05, _ProjectionParams.z, _ProjectionParams.y, 0.008, 100, 10, pt, stepCount))
                    {
                        pt = (pt + 1.0) * 0.5;
                        col.rgb = //(rayDir + float3(1, 1, 1)) * 0.5; 
                                    /*tex2D(_MainTex, pt);*/lerp(col, tex2D(_MainTex, pt), 0.5);
                    }
                    //col.rgb = stepCount * 100.0;// float3((pt + 1.0) * 0.5, 1.0);// stepCount / 30.0;// 
                }
                //col.rgb = wsRayOri.xyz;
                //tex2D(_MainTex, i.uv);// float4(i.uv.x,i.uv.x,i.uv.x,1);
                //col.rgba = float4(linear01Depth, linear01Depth, linear01Depth, linear01Depth);
                //col.rgba = float4(i.uv, 1.0, 1.0);
                col.a = 1;
                return col;
            }
            ENDCG
        }
    }
}
